#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
//#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include "ionutils.h"
#include "ipcsocket.h"
#include "wmark_dev.h"
#include <sys/epoll.h>
#include <stdlib.h>


void write_buffer(void *buffer, unsigned long len)
{
	int i;
	unsigned char *ptr = (unsigned char *)buffer;

	if (!ptr) {
		fprintf(stderr, "<%s>: Invalid buffer...\n", __func__);
		return;
	}

	printf("Fill buffer content:\n");
	memset(ptr, 0xfd, len);
	for (i = 0; i < len; i++)
		printf("0x%x ", ptr[i]);
	printf("\n");
}

void write_buffer_str(void *buffer, unsigned long len, char *str)
{
	int i;
	unsigned char *ptr = (unsigned char *)buffer;

	if (!ptr) {
		fprintf(stderr, "<%s>: Invalid buffer...\n", __func__);
		return;
	}

	printf("Fill buffer content:\n");
	memset(ptr, 0, len);
	if (len >= strlen(str)) {
		memcpy(ptr, str, strlen(str));
	}
	printf("\n");
}

void read_buffer(void *buffer, unsigned long len)
{
	int i;
	unsigned char *ptr = (unsigned char *)buffer;

	if (!ptr) {
		fprintf(stderr, "<%s>: Invalid buffer...\n", __func__);
		return;
	}

	printf("Read buffer content:\n");
	// for (i = 0; i < len; i++)
	// 	printf("0x%x ", ptr[i]);
	// printf("\n");
}

void read_all_bufs(struct ion_buffer_info *ion_bufs)
{
	struct ion_buffer_info *ion_buf = NULL;
	unsigned char *map_buf;
	unsigned long map_len;
	int i = -1;

	printf("\n\n\n\n");
	printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
	for ( i = 0; i < DMA_BUFS_MAX_NUM; i++)
	{
		printf("--------------------> index=%d \n", i);
		ion_buf = &ion_bufs[i];
		map_buf = ion_buf->buffer;
		map_len = ion_buf->buflen;
		read_buffer(map_buf, map_len);
		printf("%s\n", map_buf);
	}

}

void write_all_bufs(struct ion_buffer_info *ion_bufs, char * str)
{
	struct ion_buffer_info *ion_buf = NULL;
	unsigned char *map_buf;
	unsigned long map_len;
	int i = -1;

	// printf("\n\n\n\n");
	// printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
	for ( i = 0; i < DMA_BUFS_MAX_NUM; i++)
	{
		// printf("--------------------> index=%d \n", i);
		ion_buf = &ion_bufs[i];
		map_buf = ion_buf->buffer;
		map_len = ion_buf->buflen;
		write_buffer_str(map_buf, map_len, str);
		// printf("--------------------> index=%d map_buf=%p map_len=%d str=%s\n", i, map_buf, map_len, str);
	}

}

int ion_export_buffer_fd(struct ion_buffer_info *ion_info)
{
	int i, ret, ionfd, buffer_fd;
	unsigned int heap_id;
	unsigned long maplen;
	unsigned char *map_buffer;
	struct ion_allocation_data alloc_data;
	struct ion_heap_query query;
	struct ion_heap_data heap_data[MAX_HEAP_COUNT];

	if (!ion_info) {
		fprintf(stderr, "<%s>: Invalid ion info\n", __func__);
		return -1;
	}

	/* Create an ION client */
	ionfd = open(ION_DEVICE, O_RDWR);
	if (ionfd < 0) {
		fprintf(stderr, "<%s>: Failed to open ion client: %s\n",
			__func__, strerror(errno));
		return -1;
	}

	memset(&query, 0, sizeof(query));
	query.cnt = MAX_HEAP_COUNT;
	query.heaps = (unsigned long int)&heap_data[0];
	/* Query ION heap_id_mask from ION heap */
	ret = ioctl(ionfd, ION_IOC_HEAP_QUERY, &query);
	if (ret < 0) {
		fprintf(stderr, "<%s>: Failed: ION_IOC_HEAP_QUERY: %s\n",
			__func__, strerror(errno));
		goto err_query;
	}

	heap_id = MAX_HEAP_COUNT + 1;
	for (i = 0; i < query.cnt; i++) {
		printf("supported type id = %d ion_info->heap_type=%d\n", heap_data[i].type, ion_info->heap_type);
		if (heap_data[i].type == ion_info->heap_type) {
			heap_id = heap_data[i].heap_id;
			break;
		}
	}
	printf("heap_id = %d, MAX_HEAP_COUNT=%d\n", heap_id, MAX_HEAP_COUNT);
	if (heap_id > MAX_HEAP_COUNT) {
		fprintf(stderr, "<%s>: ERROR: heap type does not exists\n",
			__func__);
		goto err_heap;
	}

	alloc_data.len = ion_info->heap_size;
	alloc_data.heap_id_mask = 1 << heap_id;
	alloc_data.flags = ion_info->flag_type;
	printf("heap_id=%d flags=%d\n", heap_id, ion_info->flag_type);
	
	/* Allocate memory for this ION client as per heap_type */
	ret = ioctl(ionfd, ION_IOC_ALLOC, &alloc_data);
	if (ret < 0) {
		fprintf(stderr, "<%s>: Failed: ION_IOC_ALLOC: %s\n",
			__func__, strerror(errno));
		goto err_alloc;
	}

	/* This will return a valid buffer fd */
	buffer_fd = alloc_data.fd;
	maplen = alloc_data.len;

	if (buffer_fd < 0 || maplen <= 0) {
		fprintf(stderr, "<%s>: Invalid map data, fd: %d, len: %ld\n",
			__func__, buffer_fd, maplen);
		goto err_fd_data;
	}

	/* Create memory mapped buffer for the buffer fd */
	map_buffer = (unsigned char *)mmap(NULL, maplen, PROT_READ|PROT_WRITE,
			MAP_SHARED, buffer_fd, 0);
	if (map_buffer == MAP_FAILED) {
		fprintf(stderr, "<%s>: Failed: mmap: %s\n",
			__func__, strerror(errno));
		goto err_mmap;
	}

	ion_info->ionfd = ionfd;
	ion_info->buffd = buffer_fd;
	ion_info->buffer = map_buffer;
	ion_info->buflen = maplen;

	return 0;

	munmap(map_buffer, maplen);

err_fd_data:
err_mmap:
	/* in case of error: close the buffer fd */
	if (buffer_fd)
		close(buffer_fd);

err_query:
err_heap:
err_alloc:
	/* In case of error: close the ion client fd */
	if (ionfd)
		close(ionfd);

	return -1;
}


int ion_export_one_buffer_fd(int ionfd, struct ion_buffer_info *ion_info)
{
	int i, ret, buffer_fd;
	unsigned int heap_id;
	unsigned long maplen;
	unsigned char *map_buffer;
	struct ion_allocation_data alloc_data;
	struct ion_heap_query query;
	struct ion_heap_data heap_data[MAX_HEAP_COUNT];

	if (!ion_info) {
		fprintf(stderr, "<%s>: Invalid ion info\n", __func__);
		return -1;
	}

	

	memset(&query, 0, sizeof(query));
	query.cnt = MAX_HEAP_COUNT;
	query.heaps = (unsigned long int)&heap_data[0];
	/* Query ION heap_id_mask from ION heap */
	ret = ioctl(ionfd, ION_IOC_HEAP_QUERY, &query);
	if (ret < 0) {
		fprintf(stderr, "<%s>: Failed: ION_IOC_HEAP_QUERY: %s\n",
			__func__, strerror(errno));
		goto err_query;
	}

	heap_id = MAX_HEAP_COUNT + 1;
	for (i = 0; i < query.cnt; i++) {
		printf("supported type id = %d ion_info->heap_type=%d\n", heap_data[i].type, ion_info->heap_type);
		if (heap_data[i].type == ion_info->heap_type) {
			heap_id = heap_data[i].heap_id;
			break;
		}
	}
	printf("heap_id = %d, MAX_HEAP_COUNT=%d\n", heap_id, MAX_HEAP_COUNT);
	if (heap_id > MAX_HEAP_COUNT) {
		fprintf(stderr, "<%s>: ERROR: heap type does not exists\n",
			__func__);
		goto err_heap;
	}

	alloc_data.len = ion_info->heap_size;
	alloc_data.heap_id_mask = 1 << heap_id;
	alloc_data.flags = ion_info->flag_type;
	printf("heap_id=%d flags=%d\n", heap_id, ion_info->flag_type);
	
	/* Allocate memory for this ION client as per heap_type */
	ret = ioctl(ionfd, ION_IOC_ALLOC, &alloc_data);
	if (ret < 0) {
		fprintf(stderr, "<%s>: Failed: ION_IOC_ALLOC: %s\n",
			__func__, strerror(errno));
		goto err_alloc;
	}

	/* This will return a valid buffer fd */
	buffer_fd = alloc_data.fd;
	maplen = alloc_data.len;

	if (buffer_fd < 0 || maplen <= 0) {
		fprintf(stderr, "<%s>: Invalid map data, fd: %d, len: %ld\n",
			__func__, buffer_fd, maplen);
		goto err_fd_data;
	}

	/* Create memory mapped buffer for the buffer fd */
	map_buffer = (unsigned char *)mmap(NULL, maplen, PROT_READ|PROT_WRITE,
			MAP_SHARED, buffer_fd, 0);
	if (map_buffer == MAP_FAILED) {
		fprintf(stderr, "<%s>: Failed: mmap: %s\n",
			__func__, strerror(errno));
		goto err_mmap;
	}

	ion_info->ionfd = ionfd;
	ion_info->buffd = buffer_fd;
	ion_info->buffer = map_buffer;
	ion_info->buflen = maplen;

	return 0;

	munmap(map_buffer, maplen);

err_fd_data:
err_mmap:
	/* in case of error: close the buffer fd */
	if (buffer_fd)
		close(buffer_fd);

err_query:
err_heap:
err_alloc:
	/* In case of error: close the ion client fd */
	if (ionfd)
		close(ionfd);

	return -1;
}

// 导出多个 
int ion_export_buffer_fds(struct ion_buffer_info *ion_infos)
{
	int ionfd = -1;
	int i = -1;
	struct ion_buffer_info *ion_info = NULL;
	/* Create an ION client */
	ionfd = open(ION_DEVICE, O_RDWR);
	if (ionfd < 0) {
		fprintf(stderr, "<%s>: Failed to open ion client: %s\n",
			__func__, strerror(errno));
		return -1;
	}

	for (i = 0; i < DMA_BUFS_MAX_NUM; i++)
	{
		ion_info = &ion_infos[i];
		ion_export_one_buffer_fd( ionfd, ion_info);
	}
	return 0;
}



int ion_import_buffer_fd(struct ion_buffer_info *ion_info)
{
	int buffd;
	unsigned char *map_buf;
	unsigned long map_len;

	if (!ion_info) {
		fprintf(stderr, "<%s>: Invalid ion info\n", __func__);
		return -1;
	}

	map_len = ion_info->buflen;
	buffd = ion_info->buffd;

	if (buffd < 0 || map_len <= 0) {
		fprintf(stderr, "<%s>: Invalid map data, fd: %d, len: %ld\n",
			__func__, buffd, map_len);
		goto err_buffd;
	}
	
	map_buf = (unsigned char *)mmap(NULL, map_len, PROT_READ|PROT_WRITE,
			MAP_SHARED, buffd, 0);
	if (map_buf == MAP_FAILED) {
		printf("<%s>: Failed - mmap fd=%d len=%d: %s\n",
			__func__, buffd, map_len, strerror(errno));
		goto err_mmap;
	}

	ion_info->buffer = map_buf;
	ion_info->buflen = map_len;

	return 0;

err_mmap:
	if (buffd)
		close(buffd);

err_buffd:
	return -1;
}


int ion_import_buffer_fds(struct ion_buffer_info *ion_infos)
{
	int i = -1;
	struct ion_buffer_info *ion_info = NULL;

	for (i = 0; i < DMA_BUFS_MAX_NUM; i++)
	{
		ion_info = &ion_infos[i];
		ion_import_buffer_fd(ion_info);
	}
	return 0;
}
void ion_close_buffer_fd(struct ion_buffer_info *ion_info)
{
	if (ion_info) {
		/* unmap the buffer properly in the end */
		munmap(ion_info->buffer, ion_info->buflen);
		/* close the buffer fd */
		if (ion_info->buffd > 0)
			close(ion_info->buffd);
		/* Finally, close the client fd */
		if (ion_info->ionfd > 0)
			close(ion_info->ionfd);
	}
}

void ion_close_buffer_fds(struct ion_buffer_info *ion_infos)
{
	int i = -1;
	struct ion_buffer_info *ion_info = NULL;

	for (i = 0; i < DMA_BUFS_MAX_NUM; i++)
	{
		ion_info = &ion_infos[i];
		ion_close_buffer_fd(ion_info);
	}
}

int socket_send_fd(struct socket_info *info)
{
	int status;
	int fd, sockfd;
	struct socketdata skdata;

	if (!info) {
		fprintf(stderr, "<%s>: Invalid socket info\n", __func__);
		return -1;
	}

	sockfd = info->sockfd;
	fd = info->datafd;
	memset(&skdata, 0, sizeof(skdata));
	skdata.data = fd;
	skdata.len = sizeof(skdata.data);
	status = sendtosocket(sockfd, &skdata);
	if (status < 0) {
		fprintf(stderr, "<%s>: Failed: sendtosocket\n", __func__);
		return -1;
	}

	return 0;
}

int socket_receive_fd(struct socket_info *info)
{
	int status;
	int fd, sockfd;
	struct socketdata skdata;

	if (!info) {
		fprintf(stderr, "<%s>: Invalid socket info\n", __func__);
		return -1;
	}

	sockfd = info->sockfd;
	memset(&skdata, 0, sizeof(skdata));
	status = receivefromsocket(sockfd, &skdata);
	if (status < 0) {
		fprintf(stderr, "<%s>: Failed: receivefromsocket\n", __func__);
		return -1;
	}

	fd = (int)skdata.data;
	info->datafd = fd;

	return status;
}

// flag: 
// EPOLLIN 可读
// EPOLLOUT 可写
int hanle_poll(int wmark_dev_fd, struct ion_buffer_info *ion_bufs, int flag, poll_fn_hanle_t hanle)
{
	struct epoll_event ev, events[MAX_EVENTS];
	int listen_sock, conn_sock, nfds, epollfd;
	int n = 0;

	/* Code to set up listening socket, 'listen_sock',
              (socket(), bind(), listen()) omitted */

	epollfd = epoll_create1(0);
	if (epollfd == -1)
	{
		perror("epoll_create1");
		exit(EXIT_FAILURE);
	}

	ev.events = flag;
	ev.data.fd = wmark_dev_fd;
	if (epoll_ctl(epollfd, EPOLL_CTL_ADD, wmark_dev_fd, &ev) == -1)
	{
		perror("epoll_ctl: listen_sock");
		exit(EXIT_FAILURE);
	}

	for (;;)
	{
		nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
		if (nfds == -1)
		{
			perror("epoll_wait");
			exit(EXIT_FAILURE);
		}

		for (n = 0; n < nfds; ++n)
		{
			if (events[n].data.fd == wmark_dev_fd)
			{
				printf("event -> %s\n", flag == EPOLLOUT ? "EPOLLOUT" : "EPOLLIN");
				hanle( wmark_dev_fd, ion_bufs);
			}
			else
			{
				printf("%s %d\n", __func__, __LINE__);
			}
		}
	}
	return 0;
}